package com.gzc.just.play.last.war.common.share.distributed;

import com.gzc.just.play.last.war.common.service.ServerRegistryService;
import com.gzc.just.play.last.war.common.distributed.ServiceInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Load balancer
 * Distributes requests across multiple server instances
 */
@Component
public class LoadBalancer {
    
    private static final Logger log = LoggerFactory.getLogger(LoadBalancer.class);
    
    private final ServerRegistryService serverRegistryService;
    
    // Load balancing round-robin counter
    private final AtomicInteger roundRobinCounter = new AtomicInteger(0);
    
    public LoadBalancer(ServerRegistryService serverRegistryService) {
        this.serverRegistryService = serverRegistryService;
    }
    
    /**
     * Choose random instance
     * @param serviceName Service name
     * @return Chosen instance
     */
    public ServiceInstance chooseRandom(String serviceName) {
        List<ServiceInstance> instances = serverRegistryService.getInstances(serviceName);
        if (instances == null || instances.isEmpty()) {
            log.warn("No available instances for service: {}", serviceName);
            return null;
        }
        
        int index = ThreadLocalRandom.current().nextInt(instances.size());
        ServiceInstance instance = instances.get(index);
        log.debug("Chose instance {} for service {} using random algorithm", 
                 instance.getHost() + ":" + instance.getPort(), serviceName);
        return instance;
    }
    
    /**
     * Choose instance using round-robin
     * @param serviceName Service name
     * @return Chosen instance
     */
    public ServiceInstance chooseRoundRobin(String serviceName) {
        List<ServiceInstance> instances = serverRegistryService.getInstances(serviceName);
        if (instances == null || instances.isEmpty()) {
            log.warn("No available instances for service: {}", serviceName);
            return null;
        }
        
        int index = roundRobinCounter.getAndIncrement() % instances.size();
        ServiceInstance instance = instances.get(index);
        log.debug("Chose instance {} for service {} using round-robin algorithm", 
                 instance.getHost() + ":" + instance.getPort(), serviceName);
        return instance;
    }
    
    /**
     * Choose instance based on load
     * @param serviceName Service name
     * @return Chosen instance
     */
    public ServiceInstance chooseByLoad(String serviceName) {
        List<ServiceInstance> instances = serverRegistryService.getInstances(serviceName);
        if (instances == null || instances.isEmpty()) {
            log.warn("No available instances for service: {}", serviceName);
            return null;
        }
        
        // Choose instance with lowest load
        ServiceInstance chosen = instances.stream()
                .min((i1, i2) -> {
                    double load1 = i1.getLoadInfo().getLoadFactor();
                    double load2 = i2.getLoadInfo().getLoadFactor();
                    return Double.compare(load1, load2);
                })
                .orElse(null);
                
        if (chosen != null) {
            log.debug("Chose instance {} for service {} using load-based algorithm", 
                     chosen.getHost() + ":" + chosen.getPort(), serviceName);
        }
        
        return chosen;
    }
    
    /**
     * Get all server instances
     * @param serviceName Service name
     * @return All server instances
     */
    public List<ServiceInstance> getInstances(String serviceName) {
        return serverRegistryService.getAllServerInfo().values().stream()
                .filter(info -> serviceName.equals(info.getServiceName()))
                .map(info -> {
                    ServiceInstance instance = new ServiceInstance();
                    instance.setInstanceId(info.getServerId());
                    instance.setServiceName(info.getServiceName());
                    instance.setHost(info.getHost());
                    instance.setPort(info.getPort());
                    return instance;
                })
                .toList();
    }
}